package com.hjt.gulimall.product.web;

import com.hjt.gulimall.product.entity.CategoryEntity;
import com.hjt.gulimall.product.service.CategoryService;
import com.hjt.gulimall.product.vo.Catelog2Vo;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 商城首页
 * @author:97007
 * @date:2021/9/20 14:14
 */
@Controller
public class IndexController {

    @Autowired
    CategoryService categoryService;

    @Autowired
    RedissonClient redisson;

    @Autowired
    StringRedisTemplate redisTemplate;

    @GetMapping({"/","/index.html"})
    public String indexPage(Model model){
        List<CategoryEntity> categoryEntityList = categoryService.getFirstLevelCategories();
        model.addAttribute("categories", categoryEntityList);
        return "index";
    }

    // index/json/catalog.json
    @GetMapping("/index/json/catalog.json")
    @ResponseBody
    public Map<String, List<Catelog2Vo>>  getCatelogJson() {

        Map<String, List<Catelog2Vo>>  map = categoryService.getCatelogJson();
        return map;
    }

    // 分布式锁测试
    @ResponseBody
    @GetMapping("/hello")
    public String hello() {

        // 获取锁
        RLock lock = redisson.getLock("my-lock");
        System.out.println("加锁ing。。。");
        // 阻塞等待加锁
        lock.lock();  // 默认等待30s
        // 1） 【锁自动续期】，如果业务执行过长，运行期间自动给锁增加过期时间30s。
        // 2）【默认30s过期时间】加锁的业务运行完成，就不会给当前锁续期，就不会给锁自动续期，锁就会在30s内自动过期。
//        lock.lock(10, TimeUnit.SECONDS); // 设置10s自动解锁，一定要大于业务的执行时间。指定过期时间，锁到期，不会有自动续期
        //   如果传递了锁的超时时间，就执行脚本，发送给redis执行脚本，进行占锁，默认超时时间就是指定的时间
        // 如果没有传递时间，就是获取默认的超时时间（private long lockWatchdogTimeout = 30 * 1000;）
        // 只要占锁成功，就会启动一个定时任务，重新给锁设置过期时间【lockWatchdogTimeout】30s
        // internalLockLeaseTime（【lockWatchdogTimeout】为看门狗的时间） / 3 的时间触发一次自动续期，默认为10s

        // 最佳实践
        // 使用lock.lock(10, TimeUnit.SECONDS); 省去自动续期的操作
        try {
            System.out.println("执行业务逻辑" + Thread.currentThread().getName());
            Thread.sleep(30000);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            lock.unlock();
            System.out.println("释放锁");
        }
        return "hello";
    }


    // 读操作
    @ResponseBody
    @GetMapping("/read")
    public String read() {
        // 获取读锁
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        RLock lock = readWriteLock.readLock();
        lock.lock();
        System.out.println("获取到了读锁");
        String s = "";
        try {
            s = redisTemplate.opsForValue().get("rw-value");
            Thread.sleep(30000);// 模拟业务处理时间
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            // 释放读锁
            lock.unlock();
            System.out.println("释放了读锁");
        }
        return s;
    }


    //保证一定能读到最新数据，修改数据期间。写锁是一个排他锁，读锁是一个共享锁
    // 写锁没有释放，读必须等待
    // 先写再读：等待写锁释放，才能读
    // 先读再写：
    // 并发读，只会在redis中记录所有读锁，它们同时加锁
    @ResponseBody
    @GetMapping("/write")
    public String write() {
        // 获取写锁
        RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
        RLock lock = readWriteLock.writeLock();
        lock.lock();
        System.out.println("获取到了写锁");
        String s = "";
        try {
            s = UUID.randomUUID().toString();
            redisTemplate.opsForValue().set("rw-value", s);
            Thread.sleep(30000);// 模拟业务处理时间
        }
        catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            // 释放写锁
            lock.unlock();
        }
        return s;
    }


    // 分布式信号量
    // 应用场景：限流，请求服务时，查看可用的流量有多少，
    // 获取信号量
    @GetMapping("/acquire")
    @ResponseBody
    public String acquire() throws InterruptedException {
        // 获取信号量
        RSemaphore semaphore = redisson.getSemaphore("enabled-nums");
        semaphore.acquire(); // 阻塞等待
        // boolean b = semaphore.tryAcquire();// 尝试获取信号量，非阻塞
        System.out.println("执行业务操作");
        return "ok";
    }

    // 分布式信号量
    @GetMapping("/release")
    @ResponseBody
    public String release(){
        // 释放信号量
        RSemaphore semaphore = redisson.getSemaphore("enabled-nums");
        semaphore.release();
        return "ok";
    }

    /**
     * 分布式闭锁
     */
    @ResponseBody
    @GetMapping("/lockDoor")
    public String lockDoor() throws InterruptedException {
        RCountDownLatch countDownLatch = redisson.getCountDownLatch("lock-door");
        countDownLatch.trySetCount(5); // 设置等待计数
        countDownLatch.await(); // 等待闭锁都完成

        return "放假了";
    }


    @ResponseBody
    @GetMapping("/gogo/{classId}")
    public String gogo(@PathVariable String classId) {
        RCountDownLatch countDownLatch = redisson.getCountDownLatch("lock-door");
        countDownLatch.countDown(); // 计数减一
        return classId + "走人";
    }
}
